/**
 * Copyright (c) 2019-2029, wwww.kiwipeach.cn (liuburu@qq.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * https://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package cn.kiwipeach.testcase.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * JWT的组成：
 * 头部（header）：
 * { 'typ': 'JWT', 'alg': 'HS256' } ==(加密)==>  eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
 * 负载（payload）：
 * 标准中注册的声明
 * iss: jwt签发者
 * sub: jwt所面向的用户
 * aud: 接收jwt的一方
 * exp: jwt的过期时间，这个过期时间必须要大于签发时间
 * nbf: 定义在什么时间之前，该jwt都是不可用的.
 * iat: jwt的签发时间
 * jti: jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击。
 * 公共的声明
 * <p>
 * 签证（signature）：
 */
@Slf4j
public class RSAJwtTestCase {
    private static final String PUBLIC_KEY = "RSAPublicKey";
    private static final String PRIVATE_KEY = "RSAPrivateKey";
    private RSA256Key rsa256Key;

    //map对象中存放公私钥
    @BeforeEach
    private void genKeyPair() throws Exception {
        //  /** RSA算法要求有一个可信任的随机数源 */
        //获得对象 KeyPairGenerator 参数 RSA 1024个字节
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        keyPairGen.initialize(1024);
        //通过对象 KeyPairGenerator 生成密匙对 KeyPair
        KeyPair keyPair = keyPairGen.generateKeyPair();
        //通过对象 KeyPair 获取RSA公私钥对象RSAPublicKey RSAPrivateKey
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        //公私钥对象存入map中
        this.rsa256Key = new RSA256Key();
        this.rsa256Key.setPrivateKey(privateKey);
        this.rsa256Key.setPublicKey(publicKey);
    }


    @Test
    public void JWT对称机密() {
        try {
            RSAPublicKey publicKey = rsa256Key.getPublicKey();
            RSAPrivateKey privateKey = rsa256Key.getPrivateKey();
            Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);

            Map<String, Object> headerClaims = new HashMap();
            headerClaims.put("owner", "Java开源博客版权所有");

            Map<String, Object> claims = new HashMap<String, Object>();//创建payload的私有声明（根据特定的业务需要添加，如果要拿这个做验证，一般是需要和jwt的接收方提前沟通好验证方式的）
            claims.put("uid", UUID.randomUUID().toString());
            claims.put("user_name", "kiwipeach");
            claims.put("nick_name", "一直梨花压海棠");

            Date nowDate = new Date();
            long expMillis = nowDate.getTime() + 60 * 1000 * 5; //5分钟
            long notBfMillis = nowDate.getTime() + 60 * 1000; //30秒
            Date expire = new Date(expMillis);
            Date nbf = new Date(notBfMillis);
            /**
             *   参考：com.auth0.jwt.impl.PublicClaims
             *  * iss: jwt签发者
             *  * sub: jwt所面向的用户
             *  * aud: 接收jwt的一方
             *  * exp: jwt的过期时间，这个过期时间必须要大于签发时间
             *  * nbf: 定义在什么时间之前，该jwt都是不可用的.
             *  * iat: jwt的签发时间
             *  * jti: jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击。
             */
            String token = JWT.create()
                    .withHeader(headerClaims)
                    .withIssuer("nice-blog-sys")             //jwt签发者
                    .withSubject("blog common user.")            //jwt所面向的用户
                    .withExpiresAt(expire)                 //jwt的过期时间，这个过期时间必须要大于签发时间
                    //.withNotBefore(nbf)                 //定义在什么时间之前，该jwt都是不可用的.
                    .withIssuedAt(nowDate)                  //jwt的签发时间
                    .withJWTId(UUID.randomUUID().toString())
                    .withClaim("user", claims)
                    .sign(algorithm);
            log.info("签发token值:{}", token);


            //验证
            try {
                JWTVerifier verifier = JWT.require(algorithm).build(); //Reusable verifier instance
                DecodedJWT jwt = verifier.verify(token);
                log.info("获取token签发者:{}", jwt.getClaim("user").asMap());
            } catch (JWTVerificationException e) {
                //Invalid signature/claims
                e.printStackTrace();
            }
        } catch (JWTCreationException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void 验证token() throws Exception {
        RSAPublicKey publicKey = rsa256Key.getPublicKey();
        RSAPrivateKey privateKey = rsa256Key.getPrivateKey();
        Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);
        //验证
        String token = "eyJvd25lciI6IkphdmHlvIDmupDljZrlrqLniYjmnYPmiYDmnIkiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJibG9nIGNvbW1vbiB1c2VyLiIsImlzcyI6Im5pY2UtYmxvZy1zeXMiLCJleHAiOjE1ODcxNzE0MDUsImlhdCI6MTU4NzE3MTEwNSwidXNlciI6eyJ1aWQiOiJlZGIzZmU2ZC1iZTgwLTQ2ODQtODY1Ni0yNjZhYjUyYjY0MjMiLCJ1c2VyX25hbWUiOiJraXdpcGVhY2giLCJuaWNrX25hbWUiOiLkuIDnm7TmoqjoirHljovmtbfmo6AifSwianRpIjoiMjJmYTkyNzMtOGM0Yy00NDE5LTgzOTctZmM5MmM2OTAzYzljIn0.Q_VoNFB_g_GhMXoEVQ0bYcI6a1WUr8sdj-ynbnHmfu0K1UJMUOEs1gEON6N11rWyqj7qLkqGKXcDOtju4J3p-BA5KmUpdCFH8xc5siaT3ZH0eOUEircC5EtrAu-babnmIU8H12N9RH84RF7lEdLPs36drqkiHLJpmTb2vCiDF8Q";
        try {
            JWTVerifier verifier = JWT.require(algorithm)
                    .build(); //Reusable verifier instance
            DecodedJWT jwt = verifier.verify(token);
            log.info("获取token签发者:{}", jwt.getIssuer());
        } catch (JWTVerificationException e) {
            //Invalid signature/claims
            e.printStackTrace();
        }
    }


    @Test
    public void JWT解密() {
        String token = "eyJvd25lciI6IkphdmHlvIDmupDljZrlrqLniYjmnYPmiYDmnIkiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJibG9nIGNvbW1vbiB1c2VyLiIsImlzcyI6Im5pY2UtYmxvZy1zeXMiLCJleHAiOjE1ODcxNzE0MDUsImlhdCI6MTU4NzE3MTEwNSwidXNlciI6eyJ1aWQiOiJlZGIzZmU2ZC1iZTgwLTQ2ODQtODY1Ni0yNjZhYjUyYjY0MjMiLCJ1c2VyX25hbWUiOiJraXdpcGVhY2giLCJuaWNrX25hbWUiOiLkuIDnm7TmoqjoirHljovmtbfmo6AifSwianRpIjoiMjJmYTkyNzMtOGM0Yy00NDE5LTgzOTctZmM5MmM2OTAzYzljIn0.Q_VoNFB_g_GhMXoEVQ0bYcI6a1WUr8sdj-ynbnHmfu0K1UJMUOEs1gEON6N11rWyqj7qLkqGKXcDOtju4J3p-BA5KmUpdCFH8xc5siaT3ZH0eOUEircC5EtrAu-babnmIU8H12N9RH84RF7lEdLPs36drqkiHLJpmTb2vCiDF8Q";
        try {
            DecodedJWT jwt = JWT.decode(token);
            Claim user = jwt.getClaim("user");
            log.info("获取解密:{},{}", jwt.getIssuer(), user.asMap().toString());
        } catch (JWTDecodeException exception) {
            //Invalid token
            log.info("token解密失败:{}", exception.getMessage());
        }
    }

}
